{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# chap11 测试代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在编写类或者函数是，通常会编写一个测试程序。若可以通过测试，就证明可以应对用户不同情况的输入，会使你的信心大增。当往程序中新添加代码时，你也可以编写测试，以确保你的代码正确。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "程序员都会犯错，因此每个程序员都经常测试其代码，在用户使用之前发现其问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本章中你将会学习使用unittest模块中的工具来测试代码。你将会编写测试用例，核实输入和输出的正确性。你将会知道通过测试是什么样子，没有通过样例又是什么样子，还将知道样例未通过该如何改进代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.1 测试用例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先要编写要测试的代码(name_function.py)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后编写测试代码(names.py)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enter 'q' at any time to quit.\n",
      "\n",
      "Please give me a first name:be\n",
      "Please give me a last name: doom\n",
      "\tNeatly formatted name: Be Doom.\n",
      "\n",
      "Please give me a first name:q\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<module 'names' from 'C:\\\\Users\\\\MC\\\\Documents\\\\Python_Workspace\\\\notebook\\\\Python编程从入门到实践\\\\names.py'>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import names\n",
    "names"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 11.1.1 单元测试 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你可能发现这样的测试代码，并不能检查大量的数据，因为人力有限。我们是否可以实现自动化呢？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我会介绍一个新的库：**unittest**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，我们导入了模块unittest 和要测试的函数get_formatted_name() 。我们创建了一个名为TestNamesCase 的类，用于包含一系列针\n",
    "对get_formatted_name() 的单元测试。你可随便给这个类命名，但最好让它看起来与要测试的函数相关，并包含字样Test。这个类必须继承unittest.TestCase 类，这样Python才知道如何运行你编写的测试。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若是正确的话打印OK，错误的话就要告诉我什么地方错了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后我们就可以根据你的报错去修改代码了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.2 测试类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本章前半部分，你编写了针对单个函数的测试，下面来编写针对类的测试。很多程序中都会用到类，因此能够证明你的类能够正确地工作会大有裨益。如果针对类的测试通过了，你就能确信对类所做的改进没有意外地破坏其原有的行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 11.2.1 各种断言方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "断言方法就是判断你函数输出的结果和正确答案是否满足条件，若不满足，则报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. assertEqual(a, b) 判断a == b\n",
    "2. assertNotEqual(a, b) 判断 a != b\n",
    "3. assertTrue(x) 判断x为真\n",
    "4. assertFalse(x) 判断x为假\n",
    "5. assertIn(item, list) 判断item在list中\n",
    "6. assertOutIn(item, list) 判断item不在list中"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 11.2.2 一个要测试的类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类的测试方法和函数的测试方法大同小异。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面我们来编写一个要测试的类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class AnonyMouseSurvey():\n",
    "    \"\"\"收集匿名调查问卷\"\"\"\n",
    "    \n",
    "    def __init__(self, question):\n",
    "        \"\"\"存储一个问题，并为存储答案做准备\"\"\"\n",
    "        self.qusetion = question\n",
    "        self.responses = []\n",
    "    \n",
    "    def show_question(self):\n",
    "        \"\"\"显示调查问卷\"\"\"\n",
    "        print(question)\n",
    "    \n",
    "    def store_response(self, new_response):\n",
    "        \"\"\"存储单份答案\"\"\"\n",
    "        self.responses.append(new_response)\n",
    "    \n",
    "    def show_result(self):\n",
    "        \"\"\"展示回答内容\"\"\"\n",
    "        print('Survey Result:')\n",
    "        for response in self.responses:\n",
    "            print(\"- \" + response)       "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了证明这个类能够正确地工作，我编写了一个运行程序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "What language did you first learn to speak?\n",
      "Enter 'q' at any timeto exit\n",
      "\n",
      "Language: Chinese\n",
      "Language: Enlish\n",
      "Language: Bedoom\n",
      "Language: q\n",
      "Survey Result:\n",
      "- Chinese\n",
      "- Enlish\n",
      "- Bedoom\n"
     ]
    }
   ],
   "source": [
    "question = 'What language did you first learn to speak?'\n",
    "my_survey = AnonyMouseSurvey(question)\n",
    "\n",
    "my_survey.show_question()\n",
    "print(\"Enter 'q' at any timeto exit\\n\")\n",
    "while True:\n",
    "    reponse = input(\"Language: \")\n",
    "    if reponse == 'q': break\n",
    "    else: my_survey.store_response(reponse)\n",
    "\n",
    "my_survey.show_result()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 11.2.3 测试AnonyMouseSurvey类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E\n",
      "======================================================================\n",
      "ERROR: C:\\Users\\MC\\AppData\\Roaming\\jupyter\\runtime\\kernel-c65907e1-9ac1-4e73-b871-ef99b56fd030 (unittest.loader._FailedTest)\n",
      "----------------------------------------------------------------------\n",
      "AttributeError: module '__main__' has no attribute 'C:\\Users\\MC\\AppData\\Roaming\\jupyter\\runtime\\kernel-c65907e1-9ac1-4e73-b871-ef99b56fd030'\n",
      "\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.001s\n",
      "\n",
      "FAILED (errors=1)\n"
     ]
    },
    {
     "ename": "SystemExit",
     "evalue": "True",
     "output_type": "error",
     "traceback": [
      "An exception has occurred, use %tb to see the full traceback.\n",
      "\u001b[1;31mSystemExit\u001b[0m\u001b[1;31m:\u001b[0m True\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\mc\\pycharmprojects\\pythonproject\\venv\\lib\\site-packages\\IPython\\core\\interactiveshell.py:3426: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n",
      "  warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
     ]
    }
   ],
   "source": [
    "from test_survey import TestAnonmyousSurvey\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "jupyter notebook 可以不能运行这个单元测试，你可以运行一下py文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "做法的效果很好，但这些测试有些重复的地方。下面使用unittest 的另一项功能来提高它们的效率。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "11.2.4 set.up()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在前面的test_survey.py中，我们在每个测试方法中都创建了一个AnonymousSurvey 实例，并在每个方法中都创建了答案。unittest.TestCase 类包含方法setUp() ，让我们**只需创建这些对象一次**，并在每个测试方法中使用它们。如果你在TestCase 类中包含了方法setUp() ，**Python将先运行它，再运行各个以test_打头的方法**。这样，在你编写的每个测试方法中都可使用在方法setUp() 中创建的对象了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E\n",
      "======================================================================\n",
      "ERROR: C:\\Users\\MC\\AppData\\Roaming\\jupyter\\runtime\\kernel-c65907e1-9ac1-4e73-b871-ef99b56fd030 (unittest.loader._FailedTest)\n",
      "----------------------------------------------------------------------\n",
      "AttributeError: module '__main__' has no attribute 'C:\\Users\\MC\\AppData\\Roaming\\jupyter\\runtime\\kernel-c65907e1-9ac1-4e73-b871-ef99b56fd030'\n",
      "\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.001s\n",
      "\n",
      "FAILED (errors=1)\n"
     ]
    },
    {
     "ename": "SystemExit",
     "evalue": "True",
     "output_type": "error",
     "traceback": [
      "An exception has occurred, use %tb to see the full traceback.\n",
      "\u001b[1;31mSystemExit\u001b[0m\u001b[1;31m:\u001b[0m True\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\mc\\pycharmprojects\\pythonproject\\venv\\lib\\site-packages\\IPython\\core\\interactiveshell.py:3426: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n",
      "  warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
     ]
    }
   ],
   "source": [
    "import unittest\n",
    "from survey import AnonymousSurvey\n",
    "\n",
    "\n",
    "class TestAnonymousSurvey(unittest.TestCase):\n",
    "    \"\"\"针对AnonymousSurvey类的测试\"\"\"\n",
    "    \n",
    "    def setUp(self):\n",
    "        \"\"\" 创建一个调查对象和一组答案，供使用的测试方法使用\n",
    "        \"\"\"\n",
    "        question = \"What language did you first learn to speak?\" \n",
    "        self.my_survey = AnonymousSurvey(question) \n",
    "        self.responses = ['English', 'Spanish', 'Mandarin']\n",
    "\n",
    "    def test_store_single_response(self):\n",
    "        \"\"\"测试单个答案会被妥善地存储\"\"\"\n",
    "        self.my_survey.store_response(self.responses[0])\n",
    "        self.assertIn(self.responses[0], self.my_survey.responses)\n",
    "    \n",
    "    def test_store_three_responses(self):\n",
    "        \"\"\"测试三个答案会被妥善地存储\"\"\"\n",
    "        for response in self.responses:\n",
    "            self.my_survey.store_response(response)\n",
    "        for response in self.responses:\n",
    "            self.assertIn(response, self.my_survey.responses)\n",
    "\n",
    "unittest.main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.3 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本章中，你学习了：\n",
    "* 如何使用模块unittest 中的工具来为函数和类编写测试；\n",
    "* 如何编写继承unittest.TestCase 的类，以及如何编写测试方法，以核实函数和类的行为符合预期；\n",
    "* 如何使用方法setUp() 来根据类高效地创建实例并设置其属性，以便在类的所有测试方法中都可使用它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "测试是很多初学者都不熟悉的主题。作为初学者，并非必须为你尝试的所有项目编写测试；但参与工作量较大的项目时，你应对自己编写的函数和类的重要行为进行测试。这样你就能够更加确定自己所做的工作不会破坏项目的其他部分，你就能够随心所欲地改进既有代码了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果不小心破坏了原来的功能，你马上就会知道，从而能够轻松地修复问题。相比于等到不满意的用户报告bug后再采取措施，在测试未通过时采取措施要容易得多。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果你在项目中包含了初步测试，其他程序员将更敬佩你，他们将能够更得心应手地尝试使用你编写的代码，也更愿意与你合作开发项目。如果你要跟其他程序员开发的项目共享代码，就必须证明你编写的代码通过了既有测试，通常还需要为你添加的新行为编写测试。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
